package com.ldr.custom.spring.v3.beans;

import com.ldr.custom.spring.annotation.GPAutowried;
import com.ldr.custom.spring.annotation.GPController;
import com.ldr.custom.spring.annotation.GPService;
import com.ldr.custom.spring.v3.aop.GPDefaultAopProxyFactory;
import com.ldr.custom.spring.v3.aop.config.GPAopConfig;
import com.ldr.custom.spring.v3.aop.support.GPAdvisedSupport;
import com.ldr.custom.spring.v3.context.GPAbstractApplicationContext;
import org.springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * BeanFactory工厂，最终实现类
 */
public class GPDefaultListableBeanFactory extends GPAbstractApplicationContext {

    //保存配置信息
    protected Map<String, GPBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //用来保证注册式单例Bean
    private Map<String, Object> beanCacheMap = new HashMap<>();

    private Properties aopConf;

    //
    private Map<String, GPBeanWrapper> factoryBeanInstanceCache = new HashMap<>();
    //key为BeanName，value为true代表是代理类，false代表Bean
    private final Map<String, Boolean> advisedBeans = new ConcurrentHashMap<>(256);

    protected void onRefresh() {

    }

    @Override
    protected void refreshBeanFactory() {

    }

    protected Object doCreateBean(final String beanName, final GPBeanDefinition beanDefinition, final Object[] args) {

        GPBeanWrapper instanceWrapper = null;

        //判断是否是单例
        if (beanDefinition.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.get(beanName);
        }

        if (instanceWrapper == null) {
            instanceWrapper = createInstanceBean(beanDefinition);

        }

        Object bean = instanceWrapper.getWrapperInstance();
        //依赖注入
        populateBean(beanName, bean, args);
        //初始化Bean
        bean = initializeBean(beanName, bean, beanDefinition);

        instanceWrapper.setWrapperInstance(bean);

        this.factoryBeanInstanceCache.put(beanName,instanceWrapper);

        return bean;
    }

    /**
     * Bean实例化之后进行初始化，init-method
     *
     * @return
     */
    public Object initializeBean(final String beanName, final Object bean, GPBeanDefinition beanDefinition) {
        Object existingBean=bean;
        //初始化前，前置通知
        existingBean=applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        //初始化指定的方法
        invokeInitMethods(beanName,bean,beanDefinition);
        //初始化后，创建代理对象
        existingBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

        return existingBean;
    }


    /**
     * 传一个Beanfinition，就返回一个实例Bean
     *
     * @param gpBeanDefinition
     * @return
     */
    private GPBeanWrapper createInstanceBean(GPBeanDefinition gpBeanDefinition) {

        Object instance = null;

        String beanClassName = gpBeanDefinition.getBeanClassName();

        try {
            synchronized (this) {

                if (this.beanCacheMap.containsKey(beanClassName)) {
                    instance = this.beanCacheMap.get(beanClassName);
                } else {
                    Class<?> clazz = Class.forName(beanClassName);
                    instance = clazz.newInstance();
                    this.beanCacheMap.put(beanClassName, instance);
                }
            }
            return new GPBeanWrapper(instance);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        return null;
    }


    public void populateBean(String beanName, Object instance, Object[] args) {
        Class<?> clazz = instance.getClass();
        if (!clazz.isAnnotationPresent(GPController.class)
                && !clazz.isAnnotationPresent(GPService.class)) {
            return;
        }

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(GPAutowried.class)) continue;

            GPAutowried autowried = field.getAnnotation(GPAutowried.class);
            String autowriedName = autowried.value().trim();
            if ("".equals(autowriedName)) {
                autowriedName = field.getType().getName();
            }

            field.setAccessible(true);

            try {
                //把aop代理对象依赖注入进去
                Object dependBean = null;
                GPBeanWrapper wrapperInstance = this.factoryBeanInstanceCache.get(autowriedName);
                if (wrapperInstance == null) {
                    //实例化依赖类
                    dependBean = doCreateBean(autowriedName, this.beanDefinitionMap.get(autowriedName), args);
                }
                field.set(instance, dependBean);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }

    //调用初始化方法，init-method
    protected void invokeInitMethods(String beanName, final Object bean, GPBeanDefinition beanDefinition) {

    }

    //前置通知
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) {
        return null;
    }


    //后置通知，实例化对象之后，创建代理对象
    public Object applyBeanPostProcessorsAfterInitialization(Object bean, String beanName) {

        if (Boolean.FALSE.equals(this.advisedBeans.get(beanName))){
            return bean;
        }

        //初始化Aop配置
        GPAdvisedSupport gpAdvisedSupport = initializationAopConf();
        gpAdvisedSupport.setTargetClass(bean.getClass());
        gpAdvisedSupport.setTargetSource(bean);


        //判断切面表达式是否命中类
        if (gpAdvisedSupport.pointCutMatch()) {

            this.advisedBeans.put(beanName,Boolean.TRUE);
            //创建代理对象
            Object proxy = GPDefaultAopProxyFactory.getInstance().createAopProxy(gpAdvisedSupport).getProxy();
            //缓存对象状态，是否是代理对象

            //缓存代理类
            return proxy;
        }
        this.advisedBeans.put(beanName,Boolean.FALSE);
        return bean;
    }


    //初始化Aop配置
    private GPAdvisedSupport initializationAopConf() {
        GPAopConfig gpAopConfig = new GPAopConfig();
        gpAopConfig.setPointCut(aopConf.getProperty("pointCut"));
        gpAopConfig.setAspectBefore(aopConf.getProperty("aspectBefore"));
        gpAopConfig.setAspectAfter(aopConf.getProperty("aspectAfter"));
        gpAopConfig.setAspectAfterThrow(aopConf.getProperty("aspectAfterThrow"));
        gpAopConfig.setAspectAfterThrowName(aopConf.getProperty("aspectAfterThrowingName"));
        gpAopConfig.setAspectClass(aopConf.getProperty("aspectClass"));
        return new GPAdvisedSupport(gpAopConfig);
    }

    //不优雅，到时候进行修改
    public void setAopConfig(Properties aopConfig) {
        this.aopConf = aopConfig;
    }


}
